.\" Copyright (c) 2001-2020 Julien Nadeau Carriere <vedge@csoft.net>.
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistribution of source code must retain the above copyright
.\"    notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\"    notice, this list of conditions and the following disclaimer in the
.\"    documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
.\" (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd March 17, 2002
.Dt AG_OBJECT 3
.Os
.ds vT Agar API Reference
.ds oS Agar 1.0
.Sh NAME
.Nm AG_Object
.Nd agar object system
.Sh SYNOPSIS
.Bd -literal
#include <agar/core.h>
.Ed
.Sh DESCRIPTION
The Agar object system provides object-oriented programming capabilities
including inheritance and virtual functions, as well as high-level features
such as serialization, timers, VFS and abstracted data types.
It is implemented in C and provides bindings to other languages.
.Pp
Agar objects can be organized into a tree or virtual filesystem (VFS).
Any
.Nm
can become the root of a VFS.
A VFS can be made persistent to the degree required by the application.
Object data is serialized to a machine-independent binary format (using
.Xr AG_DataSource 3
calls in their
.Fn load
and
.Fn save
operations).
While an object's metadata (including the
.Va name
field) must always remain in memory, class-specific data can be serialized
to storage and deserialized on demand.
.Sh INITIALIZATION
.nr nS 1
.Ft "AG_Object *"
.Fn AG_ObjectNew "AG_Object *parent" "const char *name" "AG_ObjectClass *classInfo"
.Pp
.Ft "void"
.Fn AG_ObjectInit "AG_Object *obj" "AG_ObjectClass *classInfo"
.Pp
.Ft "void"
.Fn AG_ObjectAttach "AG_Object *newParent" "AG_Object *child"
.Pp
.Ft "void"
.Fn AG_ObjectDetach "AG_Object *child"
.Pp
.Ft "void"
.Fn AG_ObjectMoveToHead "AG_Object *obj"
.Pp
.Ft "void"
.Fn AG_ObjectMoveToTail "AG_Object *obj"
.Pp
.Ft "void"
.Fn AG_ObjectMoveUp "AG_Object *obj"
.Pp
.Ft "void"
.Fn AG_ObjectMoveDown "AG_Object *obj"
.Pp
.Ft "void"
.Fn AG_ObjectDelete "AG_Object *obj"
.Pp
.Ft "AG_Object *"
.Fn AG_ObjectRoot "AG_Object *obj"
.Pp
.Ft "AG_Object *"
.Fn AG_ObjectParent "AG_Object *obj"
.Pp
.Ft "AG_Object *"
.Fn AG_ObjectFind "AG_Object *vfsRoot" "const char *format" "..."
.Pp
.Ft "AG_Object *"
.Fn AG_ObjectFindS "AG_Object *vfsRoot" "const char *name"
.Pp
.Ft "AG_Object *"
.Fn AG_ObjectFindParent "AG_Object *obj" "const char *name" "const char *type"
.Pp
.Ft "AG_Object *"
.Fn AG_ObjectFindChild "AG_Object *obj" "const char *name"
.Pp
.Ft "char *"
.Fn AG_ObjectGetName "AG_Object *obj"
.Pp
.Ft "int"
.Fn AG_ObjectCopyName "AG_Object *obj" "char *buf" "AG_Size bufSize"
.Pp
.Ft "void"
.Fn AG_ObjectLock "AG_Object *obj"
.Pp
.Ft "void"
.Fn AG_ObjectUnlock "AG_Object *obj"
.Pp
.Ft "void"
.Fn AG_LockVFS "AG_Object *obj"
.Pp
.Ft "void"
.Fn AG_UnlockVFS "AG_Object *obj"
.Pp
.Ft "void"
.Fn AG_ObjectSetName "AG_Object *obj" "const char *fmt" "..."
.Pp
.Ft "void"
.Fn AG_ObjectSetNameS "AG_Object *obj" "const char *name"
.Pp
.Ft "void"
.Fn AG_ObjectGenName "AG_Object *obj" "AG_ObjectClass *classInfo" "char *name" "AG_Size nameSize"
.Pp
.Ft "void"
.Fn AG_ObjectGenNamePfx "AG_Object *obj" "const char *prefix" "char *name" "AG_Size nameSize"
.Pp
.Fn AGOBJECT_FOREACH_CHILD "AG_Object *child" "AG_Object *parent" "TYPE type"
.Pp
.nr nS 0
The
.Fn AG_ObjectNew
function allocates and initializes a new object instance of the given class.
The object is attached to
.Fa parent ,
unless the argument is NULL.
If
.Fa name
is NULL then a unique name (e.g., "object0") will be generated.
If both
.Fa parent
and
.Fa name
are specified and the parent object already has a child of the given name,
.Fn AG_ObjectNew
fails and returns NULL.
.Pp
The
.Fn AG_ObjectInit
function initializes an object of a given class.
It invokes
.Fn init
for every class in the inheritance hierarchy.
.Fa name
is an optional name for the object instance relative to its parent (maximum
.Dv AG_OBJECT_NAME_MAX
characters and no
.Sq / ) .
.Fa classInfo
should point to an initialized
.Ft AG_ObjectClass
structure (see
.Sx CLASSES ) .
The
.Fa flags
argument specifies a default set of flags (see
.Sx FLAGS ) .
.Pp
.Fn AG_ObjectAttach
attaches an object
.Fa child
to another object
.Fa newParent ,
raising an
.Sq attached
event.
It is a no-op if
.Fa newParent
is NULL.
.Pp
.Fn AG_ObjectDetach
removes an object
.Fa child
from its parent (if any), cancelling scheduled
.Xr AG_Timer 3
expirations before raising
.Sq detached .
.Pp
.Fn AG_ObjectMoveUp ,
.Fn AG_ObjectMoveDown ,
.Fn AG_ObjectMoveToHead
and
.Fn AG_ObjectMoveToTail
move the object in the parent object's list of child objects.
This is useful when the ordering of objects is important.
.Pp
.Fn AG_ObjectDelete
is a shorthand for
.Fn AG_ObjectDetach
followed by
.Fn AG_ObjectDestroy .
.Pp
.Fn AG_ObjectRoot
returns a pointer to the root of the VFS which the given object is attached to.
.Fn AG_ObjectParent
returns the object's parent.
.Pp
The
.Fn AG_ObjectFind
function returns the object corresponding to the specified path name.
If there is no such object it returns NULL.
.Pp
.Fn AG_ObjectFindParent
returns the first ancestor of the object matching either the name of the
object (if
.Fa name
is non-NULL), or the inheritance hierarchy string of the parent (if
.Fa type
is non-NULL).
.Pp
.Fn AG_ObjectFindChild
searches the child objects directly under
.Fa obj
for an object called
.Fa name .
It returns a pointer to the object if found or NULL.
.Pp
.Fn AG_ObjectGetName
returns an autoallocated string containing the full pathname of an object
(relative to the root of its VFS).
If insufficient memory is available to construct the path, it fails and
returns NULL.
.Pp
.Fn AG_ObjectCopyName
copies the object's full pathname (relative to its VFS root) to a
fixed-size buffer
.Fa buf
of size
.Fa bufSize
in bytes.
It returns 0 on success or -1 if the buffer is too small for the full path.
.Pp
.Fn AG_ObjectLock
and
.Fn AG_ObjectUnlock
acquire or release the locking device associated with the given object.
This is a mutex protecting all read/write members of the
.Nm
structure, except
.Fa parent ,
.Fa root
and the list of child objects
.Fa cobjs
which are all considered part of the virtual filesystem and are instead
protected by
.Fn AG_LockVFS .
.Pp
The
.Fn AG_ObjectLock
mutex can be used as a general-purpose locking device which is guaranteed to
be held during processing of all events posted to the object as well as
during object operations such as
.Fn load
and
.Fn save .
.Pp
.Fn AG_LockVFS
and
.Fn AG_UnlockVFS
acquire or release the lock which protect the layout of the entire VFS
which
.Fa obj
is a part of.
.Pp
Note: If Agar is compiled with
.Sq --disable-threads
then
.Fn AG_ObjectLock ,
.Fn AG_ObjectUnlock ,
.Fn AG_LockVFS
and
.Fn AG_UnlockVFS
become no-ops.
.Pp
.Fn AG_ObjectSetName
updates the name of the given object.
If the object is attached to a VFS then the VFS must be locked.
.Pp
.Fn AG_ObjectGenName
generates a unique name for a child object of
.Fa obj .
The class name in lowercase is used as prefix, followed by a number.
The generatedstring is copied to the fixed-size buffer
.Fa name
of size
.Fa nameSize
in bytes.
The
.Fn AG_ObjectGenNamePfx
variant generates a name using the specified prefix instead of the class name.
.Pp
The
.Fn AGOBJECT_FOREACH_CHILD
macro iterates
.Fa child
over every child object of
.Fa parent .
The
.Fa child
pointer is cast to the given structure
.Fa type ,
without type checking.
Example:
.Bd -literal -offset indent
struct my_class *chld;

AGOBJECT_FOREACH_CHILD(chld, parent, my_class) {
	printf("Child object: %s\\n", AGOBJECT(chld)->name);
}
.Ed
.Sh CLASSES
.nr nS 1
.Ft "void"
.Fn AG_RegisterClass "AG_ObjectClass *classInfo"
.Pp
.Ft "void"
.Fn AG_UnregisterClass "AG_ObjectClass *classInfo"
.Pp
.Ft "AG_ObjectClass *"
.Fn AG_CreateClass "const char *classSpec" "AG_Size objectSize" "AG_Size classSize" "Uint major" "Uint minor"
.Pp
.Ft AG_ObjectInitFn
.Fn AG_ClassSetInit "AG_ObjectClass *cl" "AG_ObjectInitFn fn"
.Pp
.Ft AG_ObjectResetFn
.Fn AG_ClassSetReset "AG_ObjectClass *cl" "AG_ObjectResetFn fn"
.Pp
.Ft AG_ObjectDestroyFn
.Fn AG_ClassSetDestroy "AG_ObjectClass *cl" "AG_ObjectDestroyFn fn"
.Pp
.Ft AG_ObjectLoadFn
.Fn AG_ClassSetLoad "AG_ObjectClass *cl" "AG_ObjectLoadFn fn"
.Pp
.Ft AG_ObjectSaveFn
.Fn AG_ClassSetSave "AG_ObjectClass *cl" "AG_ObjectSaveFn fn"
.Pp
.Ft AG_ObjectEditFn
.Fn AG_ClassSetEdit "AG_ObjectClass *cl" "AG_ObjectEditFn fn"
.Pp
.Ft "void"
.Fn AG_DestroyClass "AG_ObjectClass *cl"
.Pp
.Ft "void"
.Fn AG_RegisterNamespace "const char *name" "const char *prefix" "const char *url"
.Pp
.Ft "void"
.Fn AG_UnregisterNamespace "const char *name"
.Pp
.Ft "AG_ObjectClass *"
.Fn AG_LookupClass "const char *classSpec"
.Pp
.Ft "AG_ObjectClass *"
.Fn AG_LoadClass "const char *classSpec"
.Pp
.Ft "void"
.Fn AG_RegisterModuleDirectory "const char *path"
.Pp
.Ft "void"
.Fn AG_UnregisterModuleDirectory "const char *path"
.Pp
.Ft "int"
.Fn AG_OfClass "AG_Object *obj" "const char *pattern"
.Pp
.Ft "char *"
.Fn AG_ObjectGetClassName "const AG_Object *obj" "int full"
.Pp
.Ft "AG_ObjectClass *"
.Fn AG_ObjectSuperclass "const AG_Object *obj"
.Pp
.Ft "int"
.Fn AG_ObjectGetInheritHier "AG_Object *obj" "AG_ObjectClass **pHier" "int *nHier"
.Pp
.Fn AGOBJECT_FOREACH_CLASS "AG_Object *child" "AG_Object *parent" "TYPE type" "const char *pattern"
.Pp
.nr nS 0
The
.Fn AG_RegisterClass
function registers a new object class.
.\" MANLINK(AG_Class)
.\" MANLINK(AG_Classes)
.\" MANLINK(AG_ObjectClass)
.Fa classInfo
should be an initialized
.Ft AG_ObjectClass
structure:
.Bd -literal
typedef struct ag_object_class {
	char hier[AG_OBJECT_HIER_MAX];	/* Full inheritance hierarchy */
	AG_Size size;             	/* Size of instance structure */
	AG_Version ver;          	/* Version numbers */
	void (*init)(void *obj);
	void (*reset)(void *obj);
	void (*destroy)(void *obj);
	int  (*load)(void *obj, AG_DataSource *ds, const AG_Version *ver);
	int  (*save)(void *obj, AG_DataSource *ds);
	void *(*edit)(void *obj);
	/* ... */
} AG_ObjectClass;
.Ed
.Pp
For example:
.Bd -literal
AG_ObjectClass MyClass = {
	"MyClass",
	sizeof(MyClass),
	{ 0,0 },
	Init,
	NULL,	/* reset */
	NULL,	/* destroy */
	Load,
	Save,
	NULL	/* edit */
};
.Ed
.Pp
We can define new operations (or other class-specific data) by overloading
.Ft AG_ObjectClass .
The
.Ft AG_WidgetClass
class in Agar-GUI, for instance, overloads
.Ft AG_ObjectClass
and adds 3 new methods:
.Bd -literal
typedef struct ag_widget_class {
	struct ag_object_class _inherit;
	void (*draw)(void *);
	void (*size_request)(void *, AG_SizeReq *);
	int  (*size_allocate)(void *, const AG_SizeAlloc *);
} AG_WidgetClass;
.Ed
.Pp
For example:
.Bd -literal
AG_WidgetClass agButtonClass = {
	{
		"AG_Widget:AG_Button",  /* or "Agar(Widget:Button)" */
		sizeof(AG_Button),
		{ 0,0 },
		Init,
		NULL,	/* reset */
		NULL,	/* destroy */
		NULL,	/* load */
		NULL,	/* save */
		NULL	/* edit */
	},
	Draw,
	SizeRequest,
	SizeAllocate
};
.Ed
.Pp
The first field
.Va hier
is the inheritance hierarchy string.
For example, "AG_Widget:AG_Button" says that
.Ft AG_Button
is a direct subclass of
.Ft AG_Widget
(and
.Ft AG_Widget
is implicitely a subclass of
.Nm ) .
.Pp
Alternatively, if a namespace called "Agar" exists and is mapped to the
"AG_" prefix then the inheritance hierarchy can be also written as
"Agar(Widget:Button)".
If implementing the class requires specific libraries available as dynamically
loaded modules via
.Xr AG_DSO 3 ,
this can be indicated in the
.Va hier
string by a terminating "@" followed by one or more library names, separated
by commas.
For example:
.Bd -literal
    "AG_Widget:MY_Widget@myLib,myOtherLib"
.Ed
.Pp
The
.Va size
member specifies the size in bytes of the object instance structure.
.Va ver
is an optional datafile version number (see
.Xr AG_Version 3 ) .
.Pp
.Fn init
initializes a new object instance.
It is called after successful allocation of a new object by
.Fn AG_ObjectNew
or
.Fn AG_ObjectInit .
.Pp
.Fn reset
restores the state of the object to an initial state.
It is invoked by
.Fn AG_ObjectLoad
prior to
.Fn load ,
and also by
.Fn AG_ObjectDestroy
prior to
.Fn destroy .
.Pp
.Fn destroy
frees all resources allocated by
.Fn init
(excluding any which were freed previously by
.Fn reset ) .
.Pp
.Fn load
reads the serialized state of object
.Fa obj
from data source
.Fa ds .
.Fn save
saves the state of
.Fa obj
to data source
.Fa ds .
.Fn load
and
.Fn save
must both return 0 on success or -1 on failure.
See
.Xr AG_DataSource 3
and the
.Sx SERIALIZATION
section.
.Pp
.Fn edit
is an application-specific method.
In a typical Agar GUI application
.Fn edit
may generate and return an
.Xr AG_Window 3
or an
.Xr AG_Box 3 .
.Pp
.Fn AG_UnregisterClass
removes the specified object class.
.Pp
.Fn AG_CreateClass
offers an alternative to passing a statically-initialized
.Ft AG_ObjectClass
to
.Fn AG_RegisterClass .
The
.Fn AG_CreateClass
function allocates and initializes an
.Ft AG_ObjectClass
structure (or derivative thereof).
.Fn AG_ClassSetInit ,
.Fn AG_ClassSetReset ,
.Fn AG_ClassSetDestroy ,
.Fn AG_ClassSetLoad ,
.Fn AG_ClassSetSave
and
.Fn AG_ClassSetEdit
can be used to subsequently set the function pointers for the individual
operations.
They return a pointer to the previous operation.
.Fn AG_DestroyClass
unregisters and frees an auto-allocated
.Ft AG_ObjectClass
(or derivative thereof).
.Pp
.Fn AG_RegisterNamespace
registers a new namespace with the specified name, prefix and informational
URL.
For example, Agar registers its own using:
.Bd -literal -offset indent
AG_RegisterNamespace("Agar", "AG_", "http://libagar.org/");
.Ed
.Pp
Once the namespace is registered, it is possible to specify inheritance
hierarchies using the partitioned
.Em namespace
format:
.Bd -literal -offset indent
Agar(Widget:Button):MyLib(MyButton)
.Ed
.Pp
which is equivalent to the conventional format:
.Bd -literal -offset indent
AG_Widget:AG_Button:MY_Button
.Ed
.Pp
The
.Fn AG_UnregisterNamespace
function removes all information about the specified namespace.
.Pp
The
.Fn AG_LookupClass
function looks up the
.Ft AG_ObjectClass
structure describing the specified class (in namespace or expanded format).
If there is no currently registered class matching the specification,
.Fn AG_LookupClass
returns NULL.
.Pp
.Fn AG_LoadClass
ensures that the object class specified in
.Fa classSpec
(see
.Fn AG_RegisterClass
for details on the format) is registered, possibly loading one or more
dynamic library files if they are specified in the string.
Dynamic library dependencies are given in the form of a terminating
.Sq @lib1,lib2,...
string.
.Fn AG_LoadClass
scans the registered module directories (see
.Fn AG_RegisterModuleDirectory )
for the libraries specified in the string.
Bare library names are given (the actual filenames are platform-dependent).
Libraries that are found (and not already in memory) are loaded via
.Xr AG_DSO 3 .
The first library must define a
.Sq myFooClass
symbol (where
.Sq myFoo
is the name of the class transformed from
.Sq MY_Foo ) ,
for an
.Ft AG_ObjectClass
structure describing the class (i.e., the same structure that is passed to
.Fn AG_RegisterClass ) .
.Pp
.Fn AG_UnloadClass
unregisters the specified class and also decrements the reference count of
any dynamically-located module associated with it.
If this reference count reaches zero, the module is removed from the current
process's address space.
.Pp
The
.Fn AG_RegisterModuleDirectory
function adds the specified directory to the module search path.
.Fn AG_UnregisterModuleDirectory
removes the specified directory from the search path.
.Pp
Given an inheritance hierarchy string (with wildcards),
.Fn AG_OfClass
evaluates whether
.Fa obj
is an instance of the
specified class and returns a boolean (0 = False, 1 = True) indicating
whether the object is an instance of a matching class.
For example:
.Bd -literal
AG_Button *btn = AG_ButtonNew( ... );

if (AG_OfClass(btn, "AG_Widget:AG_Button")) {
	/*
	 * btn is an instance of AG_Button, and *not* a subclass of it.
	 */
}
if (AG_OfClass(btn, "AG_Widget:AG_Button:*")) {
	/*
	 * btn is an instance of AG_Button, or a subclass of AG_Button.
	 */
}
.Ed
.Pp
Fast paths are provided for patterns such as "Super:Sub:*" and "Super:Sub",
but patterns such as "Super:*:Sub:*" are also supported.
.Pp
.Fn AG_ObjectGetClassName
returns a newly-allocated string containing the name of the class of an
object
.Fa obj .
If
.Fa full
is 1, return the complete inheritance hierarchy (e.g., "AG_Widget:AG_Button").
Otherwise, return only the subclass (e.g., "AG_Button").
.Pp
.Fn AG_ObjectSuperclass
returns a pointer to the
.Fa AG_ObjectClass
structure describing the superclass of
.Fa obj .
If
.Fa obj
is an instance of the base class (AG_Object), then a pointer to the
.Nm
class is returned.
.Pp
The
.Fn AG_ObjectGetInheritHier
function returns into
.Fa pHier
an array of
.Ft AG_ObjectClass
pointers describing the inheritance hierarchy of an object.
The size of the array is returned into
.Fa nHier .
If the returned item count is > 0, the returned array should be freed when
no longer in use.
.Fn AG_ObjectGetInheritHier
returns 0 on success or -1 if there is insufficient memory.
.Pp
The
.Fn AGOBJECT_FOREACH_CLASS
macro iterates
.Fa child
over every child object of
.Fa parent
which is an instance of the class specified by
.Fa pattern .
.Fa child
is cast to the given structure
.Fa type .
Example:
.Bd -literal -offset indent
struct my_class *chld;

AGOBJECT_FOREACH_CLASS(chld, parent, my_class, "MyClass") {
	printf("Object %s is an instance of MyClass\\n",
	    AGOBJECT(chld)->name);
}
.Ed
.Sh RELEASING RESOURCES
.nr nS 1
.Ft "void"
.Fn AG_ObjectDestroy "AG_Object *obj"
.Pp
.Ft void
.Fn AG_ObjectReset "AG_Object *obj"
.Pp
.Ft "void"
.Fn AG_ObjectFreeEvents "AG_Object *obj"
.Pp
.Ft "void"
.Fn AG_ObjectFreeVariables "AG_Object *obj"
.Pp
.Ft "void"
.Fn AG_ObjectFreeChildren "AG_Object *obj"
.Pp
.nr nS 0
.Fn AG_ObjectReset
restores the state of an object to some initial state.
It invokes the object's
.Fn reset ,
which is expected to bring the object to a consistent state prior to
deserialization (before
.Fn load ) .
.Pp
.Fn AG_ObjectDestroy
frees all resources allocated by an object.
It invokes the
.Fn reset
and
.Fn destroy
methods over each class in the inheritance hierarchy.
.Fn AG_ObjectDestroy
also cancels any scheduled
.Xr AG_Timer 3
expiration.
.Fn AG_ObjectDestroy
implies
.Fn AG_ObjectFreeEvents ,
.Fn AG_ObjectFreeVariables
and
.Fn AG_ObjectFreeChildren .
Unless
.Dv AG_OBJECT_STATIC
is set,
.Fn AG_ObjectDestroy
also implies
.Xr free 3 .
.Pp
.Fn AG_ObjectFreeEvents
clears all configured event handlers (also cancelling any scheduled timer
expirations).
.Pp
.Fn AG_ObjectFreeVariables
clears the property table (i.e., the table of
.Xr AG_Variable 3 )
associated with the object.
.Pp
.Fn AG_ObjectFreeChildren
invokes
.Fn AG_ObjectDetach
and
.Fn AG_ObjectDestroy
on all child objects of
.Fa parent .
.Sh SERIALIZATION
.nr nS 1
.Ft "int"
.Fn AG_ObjectLoad "AG_Object *obj"
.Pp
.Ft "int"
.Fn AG_ObjectLoadFromFile "AG_Object *obj" "const char *file"
.Pp
.Ft "int"
.Fn AG_ObjectLoadFromDB "AG_Object *obj" "AG_Db *db" "const AG_Dbt *key"
.Pp
.Ft "int"
.Fn AG_ObjectLoadData "AG_Object *obj"
.Pp
.Ft "int"
.Fn AG_ObjectLoadDataFromFile "AG_Object *obj" "const char *file"
.Pp
.Ft "int"
.Fn AG_ObjectLoadGeneric "AG_Object *obj"
.Pp
.Ft "int"
.Fn AG_ObjectLoadGenericFromFile "AG_Object *obj" "const char *file"
.Pp
.Ft "int"
.Fn AG_ObjectSave "AG_Object *obj"
.Pp
.Ft "int"
.Fn AG_ObjectSaveAll "AG_Object *obj"
.Pp
.Ft "int"
.Fn AG_ObjectSaveToFile "AG_Object *obj" "const char *path"
.Pp
.Ft "int"
.Fn AG_ObjectSaveToDB "AG_Object *obj" "AG_Db *db" "const AG_Dbt *key"
.Pp
.Ft "int"
.Fn AG_ObjectSerialize "AG_Object *obj" "AG_DataSource *ds"
.Pp
.Ft "int"
.Fn AG_ObjectUnserialize "AG_Object *obj" "AG_DataSource *ds"
.Pp
.Ft "int"
.Fn AG_ObjectReadHeader "AG_DataSource *ds" "AG_ObjectHeader *header"
.Pp
.Ft "int"
.Fn AG_ObjectPageIn "AG_Object *obj"
.Pp
.Ft "int"
.Fn AG_ObjectPageOut "AG_Object *obj"
.Pp
.nr nS 0
These functions implement serialization, or archiving of the state of an
.Nm
to a flat, machine-independent binary format.
.Pp
The
.Fn AG_ObjectLoad*
family of functions load the state of an Agar object from some binary data
source.
The generic
.Nm
state is loaded first, followed by the object's serialized data (which is read
by invoking the
.Fn load
function of every class in the inheritance hierarchy).
The
.Fn AG_ObjectLoad ,
.Fn AG_ObjectLoadGeneric
and
.Fn AG_ObjectLoadData
functions look for an archive file in the default search path (using the
.Sq load-path
setting of
.Xr AG_Config 3 ) .
The
.Fn AG_ObjectLoadFromFile ,
.Fn AG_ObjectLoadGenericFromFile
and
.Fn AG_ObjectLoadDataFromFile
variants attempt to load the object state from a specific file.
The
.Fn AG_ObjectLoadFromDB
variant loads the object state from the given
.Xr AG_Db 3
database entry.
.Pp
The
.Fn AG_ObjectSave*
family of functions serialize and save the state of the given object.
The generic
.Nm
state is written first, followed by the object's serialized data
(which is written by invoking the
.Fn save
function of every class in the inheritance hierarchy).
The
.Fn AG_ObjectSave
function creates an archive of the given object in the default location
(i.e., the
.Sq save-path
setting of
.Xr AG_Config 3 ) .
The
.Fn AG_ObjectSaveAll
variant saves the object's children as well as the object itself.
.Fn AG_ObjectSaveToFile
archives the object to the specified file.
.Fn AG_ObjectSaveToDB
archives the object to the given
.Xr AG_Db 3
entry.
.Pp
The
.Fn AG_ObjectSerialize
function writes an archive of the given object to the specified
.Xr AG_DataSource 3 ,
and
.Fn AG_ObjectUnserialize
reads an archive of the given object.
.\" MANLINK(AG_ObjectHeader)
.Pp
The
.Fn AG_ObjectReadHeader
routine attempts to read the header of a serialized Agar object from a
.Xr AG_DataSource 3
and returns 0 on success or -1 if no valid header could be read.
On success, header information is returned into the
.Fa header
structure:
.Bd -literal
typedef struct ag_object_header {
	char hier[AG_OBJECT_HIER_MAX];	    /* Inheritance hierarchy */
	char libs[AG_OBJECT_LIBS_MAX];	    /* Library list */
	char classSpec[AG_OBJECT_HIER_MAX]; /* Full class specification */
	Uint32 dataOffs;                    /* Dataset offset */
	AG_Version ver;                     /* AG_Object version */
	Uint flags;                         /* Object flags */
} AG_ObjectHeader;
.Ed
.Pp
The
.Fn AG_ObjectPageIn
function loads an object's data into memory and sets the
.Dv AG_OBJECT_RESIDENT
flag.
.Fn AG_ObjectPageOut
checks whether an object is referenced by another object and if that is
not the case, the data is serialized to permanent storage, freed from
memory and
.Dv AG_OBJECT_RESIDENT
is cleared.
Both functions return 0 on success or -1 if an error has occurred.
.Sh FLAGS
The following public
.Nm
flags are defined:
.Bl -tag -width "AG_OBJECT_NON_PERSISTENT "
.It AG_OBJECT_FLOATING_VARS
Remove all entries of the
.Xr AG_Variable 3
table in
.Fn AG_ObjectLoad .
By default, the existing table is preserved and entries are created or
replaced by items found in the archive.
.It AG_OBJECT_NON_PERSISTENT
Disables archiving of the object and its children.
If set,
.Fn AG_ObjectSave
becomes a no-op and
.Fn AG_ObjectLoad
calls will fail.
.It AG_OBJECT_INDESTRUCTIBLE
Application-specific advisory flag.
.It AG_OBJECT_RESIDENT
The object's data exists in memory.
Set by
.Fn AG_ObjectPageIn
and
.Fn AG_ObjectPageOut .
.It AG_OBJECT_STATIC
Object is statically allocated (or allocated via a facility other than
.Xr malloc 3 ) .
Disable use of
.Xr free 3
by
.Fn AG_ObjectDestroy .
.It AG_OBJECT_READONLY
Application-specific advisory flag.
.It AG_OBJECT_REOPEN_ONLOAD
If an
.Fn edit
operation is defined, indicate that elements associated with its return
value (such as GUI windows or elements in the case of a GUI application)
should be recreated whenever
.Fn AG_ObjectLoad
is used.
.It AG_OBJECT_REMAIN_DATA
Prevent the object's data from being freed by
.Fn AG_ObjectReset
when a
.Fn AG_ObjectPageOut
call is made and the reference count reaches zero.
.It AG_OBJECT_DEBUG
Application-specific debugging flag.
.It AG_OBJECT_NAME_ONATTACH
Automatically generate a unique name for the object as soon as
.Fn AG_ObjectAttach
occurs.
.It AG_OBJECT_CHLD_AUTOSAVE
Serialize the object's children in
.Fn AG_ObjectSerialize .
.El
.Sh EVENTS
The
.Nm
mechanism generates the following events:
.Bl -tag -width 2n
.It Fn attached "AG_Object *parent"
The object has been attached to a new parent.
.It Fn detached "AG_Object *parent"
The object has been detached from its parent.
.It Fn renamed "void"
The object's name has changed.
.It Fn object-post-load "const char *path"
Invoked by
.Fn AG_ObjectLoadData ,
on success.
If the object was loaded from file,
.Fa path
is the pathname of the file.
.It Fn bound "AG_Variable *V"
A new variable binding has been created, or the value of an existing binding
has been updated; see
.Xr AG_Variable 3
for details.
.El
.Sh STRUCTURE DATA
For the
.Ft AG_ObjectClass
structure (see
.Sx CLASSES
section):
.Pp
.Bl -tag -compact -width "void (*destroy) "
.It Ft char *hier
Full inheritance hierarchy.
.It Ft AG_Size size
Size of instance structure (in bytes).
.It Ft AG_Version ver
Versioning information (see
.Xr AG_Version 3 ) .
.It Ft void (*init)
Initialization routine.
.It Ft void (*reset)
Cleanup routine (for
.Fn AG_ObjectReset ) .
.It Ft void (*destroy)
Final cleanup routine.
.It Ft int (*load)
Deserialization routine.
.It Ft int (*save)
Serialization routine.
.It Ft void *(*edit)
Application-specific entry point.
.El
.Pp
The following read-only members are initialized internally:
.Pp
.Bl -tag -compact -width "TAILQ(AG_ObjectClass) sub "
.It Ft char *name
The name for this class only.
.It Ft char *libs
Comma-separated list of DSO modules.
.It Ft AG_ObjectClass *super
Pointer to the superclass.
.It Ft TAILQ(AG_ObjectClass) sub
Direct subclasses of this class.
.El
.Pp
For the
.Ft AG_Object
structure:
.Bl -tag -width "char name[AG_OBJECT_NAME_MAX] "
.It Ft char name[AG_OBJECT_NAME_MAX]
Unique (in parent) identifier for this object instance.
May not contain
.Sq / .
.It Ft AG_ObjectClass *cls
A pointer to the
.Ft AG_ObjectClass
for this object's class
(see
.Sx CLASSES
section).
.It Ft Uint flags
Option flags for this object instance (see
.Sx FLAGS
section).
.It Ft TAILQ(AG_Event) events
Table of registered event handlers (set by
.Xr AG_SetEvent 3 )
and virtual functions (set by
.Fn AG_Set<Type>Fn ) .
.It Ft TAILQ(AG_Timer) timers
List of active timers (see
.Xr AG_Timer 3 ) .
.It Ft TAILQ(AG_Variable) vars
Named variables (see
.Xr AG_Variable 3 ) .
.It Ft TAILQ(AG_Object) children
List of child objects.
The
.Fn AGOBJECT_FOREACH_CHILD ,
.Fn AGOBJECT_FOREACH_CHILD_REVERSE ,
.Fn AGOBJECT_NEXT_CHILD ,
.Fn AGOBJECT_LAST_CHILD
and
.Fn AGOBJECT_FOREACH_CLASS
macros can be used to iterate over this list.
.El
.Sh EXAMPLES
The Agar GUI system represents user interfaces using a tree of
.Xr AG_Widget 3
objects attached to a parent
.Xr AG_Window 3
which is itself attached to a parent
.Xr AG_Driver 3 .
.Pp
The
.Xr SG 3
scene-graph structure of Agar-SG is a VFS of
.Xr SG_Node 3
objects.
Non-visible nodes can be paged out to storage, saving memory.
.Pp
Edacious (https://edacious.org/) represents circuits, components and simulation
data using an in-memory VFS.
Circuits are saved to a flat binary file which embeds the circuit's serialized
data with that of its sub-components (which may include third-party components,
in which case
.Nm
will autoload any required DSOs).
.Pp
See
.Pa core/dummy_object.[ch]
and
.Pa tests/objsystem*.c
in Agar sources.
.Sh SEE ALSO
.Xr AG_Event 3 ,
.Xr AG_Intro 3 ,
.Xr AG_Timer 3 ,
.Xr AG_Variable 3
.Sh HISTORY
The
.Nm
interface appeared in Agar 1.0.
.Fn AG_ObjectFreeDataset
was renamed
.Fn AG_ObjectReset
in Agar 1.6.0.
The functions
.Fn AG_CreateClass ,
.Fn AG_ClassSetInit ,
.Fn AG_ClassSetReset ,
.Fn AG_ClassSetDestroy ,
.Fn AG_ClassSetLoad ,
.Fn AG_ClassSetSave ,
.Fn AG_ClassSetEdit ,
.Fn AG_DestroyClass
and
.Fn AG_ObjectGetClassName
appeared in Agar 1.6.0.
